﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;

namespace RobotMapper
{
    public static class Mapper
    {
        public static void Initialize(Action<IRobotMapperCreator> action = null)
        {
            PartConfig.Clear();
            var robotMapperConfig = new RobotMapperCreator();
            action?.Invoke(robotMapperConfig);
        }

        #region Mapper

        public static M RobotMap<T, M>(this T source, Action<IRobotMapperConfig<T, M>> action = null)
            where T : class
            where M : class
        {
            PartConfig.Clear();
            if (source == null) return null;
            action?.Invoke(new RobotMapperConfig<T, M>(true));
            var newData = Activator.CreateInstance<M>();
            var result = CreatMappers(source, newData, action != null);
            PartConfig.Clear();
            return result;
        }

        public static List<M> RobotMap<T, M>(this List<T> source, Action<IRobotMapperConfig<T, M>> action = null)
            where T : class
            where M : class
        {
            PartConfig.Clear();
            if (source == null) return null;
            action?.Invoke(new RobotMapperConfig<T, M>(true));
            var result = new List<M>();
            source.ForEach(a =>
            {
                var newData = Activator.CreateInstance<M>();
                var newItem = CreatMappers(a, newData, action != null);
                result.Add(newItem);
            });
            PartConfig.Clear();
            return result;
        }

        public static List<M> RobotMap<T, M>(this IEnumerable<T> source, Action<IRobotMapperConfig<T, M>> action = null)
            where T : class
            where M : class
        {
            PartConfig.Clear();
            if (source == null) return null;
            action?.Invoke(new RobotMapperConfig<T, M>(true));
            var result = new List<M>();
            source.ToList().ForEach(a =>
            {
                var newData = Activator.CreateInstance<M>();
                var newItem = CreatMappers(a, newData, action != null);
                result.Add(newItem);
            });
            PartConfig.Clear();
            return result;
        }

        /// <summary>
        /// 创建映射
        /// </summary>
        /// <typeparam name="T">源</typeparam>
        /// <typeparam name="M">目标</typeparam>
        /// <param name="source">源对象</param>
        /// <param name="target">目标对象</param>
        ///// <param name="sType">源类型</param>
        ///// <param name="dType">目标类型</param>
        /// <param name="map">上级类型</param>
        /// <param name="maps">当前所有类型关系集</param>
        /// <returns></returns>
        private static M CreatMappers<T, M>(T source, M target, bool isPart = false, RelationMap map = null, List<RelationMap> maps = null)
            where T : class
            where M : class
        {
            Type sType = source.GetType();
            Type dType = target.GetType();

            try
            {
                if (source == null) return null;

                //表示第一层运行
                if (maps == null)
                {
                    maps = new List<RelationMap>();
                    map = new RelationMap()
                    {
                        Id = Guid.NewGuid(),
                        PropertyName = null,
                        SupId = null,
                        TheType = dType,
                    };
                    maps.Add(map);
                }

                //找出maps中所有的父级Maps
                var parentMaps = RobotMapperHelper.GetMapParentMaps(maps, map);

                PropertyInfo[] sourceProperties = sType.GetProperties(BindingFlags.Public | BindingFlags.Instance);
                PropertyInfo[] destinationProperties = dType.GetProperties(BindingFlags.Public | BindingFlags.Instance);
                foreach (var destinationProperty in destinationProperties)
                {
                    object oldValue = null;

                    //指定规则需要忽略的 按照规则忽略
                    var ignoreRole = ConfigHelper.GetIgnoreRole(sType, dType, isPart);
                    if (ignoreRole != null)
                    {
                        if (ignoreRole.IgnoreMembers.Contains(destinationProperty.Name))
                            continue;
                    }

                    //指定绑定规则的 按照规则来
                    var role = ConfigHelper.GetBindFuncRole(sType, dType, destinationProperty.Name, isPart);
                    if (role != null)
                    {
                        oldValue = role.SourceFunc?.Invoke(source);
                        if (oldValue == null) continue;
                        destinationProperty.SetValue(target, oldValue, null);
                        continue;
                    }

                    //源中不存在且没有绑定属性 则Ignore
                    //查询绑定属性
                    var sourceConfigPropertyBind = ConfigHelper.GetBindPropertyRole(sType, dType, destinationProperty.Name, isPart);

                    PropertyInfo sourcePropertySelf = sourceProperties.FirstOrDefault(prop => RobotMapperHelper.NameMatches(prop.Name, destinationProperty.Name));
                    PropertyInfo sourcePropertyConfig = sourceProperties.FirstOrDefault(prop => RobotMapperHelper.NameMatches(prop.Name, sourceConfigPropertyBind?.SourcePropertyName));

                    //先以绑定属性为准绑定属性没有则按同名属性
                    PropertyInfo sourceProperty = sourcePropertyConfig ?? sourcePropertySelf;
                    if (RobotMapperHelper.SetTargetValue(source, target, sourceProperty, destinationProperty))
                        continue;

                    Type sourcePropertyType = sourceProperty.PropertyType;
                    Type destinationPropertyType = destinationProperty.PropertyType;

                    oldValue = sourceProperty.GetValue(source, null);
                    //oldValue = RobotMapperHelper.GetPropertyValue<T>(source, sType, sourceProperty);
                    if (oldValue == null) continue;

                    #region 目标为数组(非系统内的 转化赋值)
                    if (destinationPropertyType.IsArray)
                    {
                        var destinationGenericType = destinationPropertyType.GetElementType();

                        //循环引用需忽略
                        if (parentMaps.Count(a => a.TheType == destinationGenericType) > 0) continue;
                        var newMapG = new RelationMap()
                        {
                            Id = Guid.NewGuid(),
                            TheType = destinationGenericType,
                            PropertyName = destinationProperty.Name,
                            SupId = map.Id,
                        };
                        maps.Add(newMapG);

                        //非系统对象则转化赋值
                        var sValue = oldValue as IEnumerable;
                        //创建目标值集合
                        var dValue = Activator.CreateInstance<ArrayList>();
                        foreach (var item in sValue)
                        {
                            //创建目标对象
                            var dNewValue = Activator.CreateInstance(destinationGenericType);
                            //转换目标对象
                            dNewValue = CreatMappers(item, dNewValue, false, newMapG, maps);
                            if (dNewValue == null) continue;
                            //将目标对象加入目标对象集合
                            dValue.Add(dNewValue);
                        }
                        var newDValue = RobotMapperHelper.InvokeExtensionMethod(dValue, destinationGenericType, "OfType");
                        var objNewDValue = RobotMapperHelper.InvokeExtensionMethod(newDValue, destinationGenericType, "ToList");
                        var newObjValue = RobotMapperHelper.InvokeExtensionMethod(newDValue, destinationGenericType, "ToArray");
                        destinationProperty.SetValue(target, newObjValue, null);
                        continue;
                    }
                    #endregion

                    #region 属性为非系统泛型集合(非接口)
                    if (destinationPropertyType.IsGenericType && !destinationPropertyType.IsInterface && (typeof(IEnumerable).IsAssignableFrom(destinationPropertyType)))
                    {
                        var destinationGenericType = destinationPropertyType.GetGenericArguments()[0];
                        var sourceGenericType = sourcePropertyType.GetGenericArguments()[0];

                        //循环引用需忽略
                        if (parentMaps.Count(a => a.TheType == destinationGenericType) > 0) continue;
                        var newMapG = new RelationMap()
                        {
                            Id = Guid.NewGuid(),
                            TheType = destinationGenericType,
                            PropertyName = destinationProperty.Name,
                            SupId = map.Id,
                        };
                        maps.Add(newMapG);

                        //非系统对象则转化赋值
                        var sValue = oldValue as IEnumerable;
                        //创建目标值集合
                        var dValue = Activator.CreateInstance(destinationPropertyType) as IList;
                        foreach (var item in sValue)
                        {
                            //创建目标对象
                            var dNewValue = Activator.CreateInstance(destinationGenericType);
                            //转换目标对象
                            dNewValue = CreatMappers(item, dNewValue, false, newMapG, maps);
                            if (dNewValue == null) continue;
                            //将目标对象加入目标对象集合
                            dValue.Add(dNewValue);
                        }
                        destinationProperty.SetValue(target, dValue, null);
                        continue;
                    }
                    #endregion

                    #region 属性为非系统泛型集合(接口 支持 IList ICollection IEnumerable)
                    if (destinationPropertyType.IsInterface &&
                        (destinationPropertyType.GetGenericTypeDefinition().Equals(typeof(IList<>)) ||
                        destinationPropertyType.GetGenericTypeDefinition().Equals(typeof(ICollection<>)) ||
                        destinationPropertyType.GetGenericTypeDefinition().Equals(typeof(IEnumerable<>))))
                    {
                        var destinationGenericType = destinationPropertyType.GetGenericArguments()[0];
                        var sourceGenericType = sourcePropertyType.GetGenericArguments()[0];

                        //循环引用需忽略
                        if (parentMaps.Count(a => a.TheType == destinationGenericType) > 0) continue;
                        var newMapG = new RelationMap()
                        {
                            Id = Guid.NewGuid(),
                            TheType = destinationGenericType,
                            PropertyName = destinationProperty.Name,
                            SupId = map.Id,
                        };
                        maps.Add(newMapG);

                        var sValue = oldValue as IEnumerable;

                        //创建目标值集合
                        var dValue = Activator.CreateInstance<ArrayList>();
                        foreach (var item in sValue)
                        {
                            //创建目标对象
                            var dNewValue = Activator.CreateInstance(destinationGenericType);
                            //转换目标对象
                            dNewValue = CreatMappers(item, dNewValue, false, newMapG, maps);
                            if (dNewValue == null) continue;
                            if (dNewValue == null) continue;
                            //将目标对象加入目标对象集合
                            dValue.Add(dNewValue);
                        }
                        var newDValue = RobotMapperHelper.InvokeExtensionMethod(dValue, destinationGenericType, "OfType");
                        object objNewDValue = null;
                        if (destinationPropertyType.GetGenericTypeDefinition().Equals(typeof(IList<>)))
                            objNewDValue = RobotMapperHelper.InvokeExtensionMethod(newDValue, destinationGenericType, "ToList");
                        if (destinationPropertyType.GetGenericTypeDefinition().Equals(typeof(ICollection<>)))
                            objNewDValue = RobotMapperHelper.InvokeExtensionMethod(newDValue, destinationGenericType, "ToList");
                        if (destinationPropertyType.GetGenericTypeDefinition().Equals(typeof(IEnumerable<>)))
                            objNewDValue = RobotMapperHelper.InvokeExtensionMethod(newDValue, destinationGenericType, "ToList");
                        destinationProperty.SetValue(target, objNewDValue, null);
                        continue;
                    }
                    #endregion

                    //循环引用需忽略
                    if (parentMaps.Count(a => a.TheType == destinationPropertyType) > 0) continue;
                    var newMap = new RelationMap()
                    {
                        Id = Guid.NewGuid(),
                        TheType = destinationPropertyType,
                        PropertyName = destinationProperty.Name,
                        SupId = map.Id,
                    };
                    maps.Add(newMap);

                    //非系统对象则转化赋值
                    var newO = Activator.CreateInstance(destinationPropertyType);
                    newO = CreatMappers(oldValue, newO, false, newMap, maps);
                    destinationProperty.SetValue(target, newO, null);
                }

                return target;
            }
            catch (Exception ex)
            {
                throw new Exception(string.Format("在转化类型:{0}->{1}发生错误:{2}", sType, dType, ex.Message));
            }
        }
        #endregion
    }
}
